package main

import (
	"fmt"
	"strings"
)

//闭包  函数+外部变量的引用
// 一个外层函数中有内层函数，改内层函数中会操作外层函数的局部变量，并且改外层函数的返回值就是这个内层函数
// 局部变量的声明周期会发生改变，正常的局部变量随着函数的调用而创建，随着函数创建而销毁
// 但在闭包结构中，外层函数的局部变量不会随着外层函数的结束而销毁，因为内层函数还要使用
// 底层原理  函数可以作为返回值   函数内部查找变量的顺序 先在自己内部查找，当找不到时在往外查找
// func adder1() func(int) int{
// 	var x int =100
// 	return func(y int)int{
// 		x +=y
// 		return x
// 	}
// }

// func adder2(x int) func(int) int {
// 	return func (y int) int  {
// 		x+=y
// 		return x
// 	}
// }
// 案列2  要求f1(f2) 因为f1中带的参数是无参函数类型，而f2是带有两个参数的，所以不能直接将f2传给f1.
// 闭包    解决两个带不同类型参数的函数合并问题
func f1(f func()) { //无参数无返回值
	fmt.Println("this is f1")
	f()
}

func f2(x, y int) { //两个int参数无返回值
	fmt.Println("this is f2")
	fmt.Println(x + y)
}

func f3(f func(int, int), x, y int) func() {
	return func() {
		// fmt.Println("this is f3")
		// fmt.Println(x+y)
		f(x, y)
	}
}

//案例三 闭包函数的计数器
func foo() {
	fmt.Println("foo功能函数")
}
func getCalledNum(f func()) func() {
	var count = 0
	inner := func() {
		f()
		count++
		fmt.Printf("函数被调用%d次\n", count)
	}
	return inner
}

//案例四  加后缀
func makeSuffixFunc(suffix string) func(string) string {
	return func(s string) string {
		if !strings.HasSuffix(s, suffix) { //如果不是以指定的字符结尾的，则返回传入的形参+后缀
			return s + suffix
		}
		return s //如果是以指定的字符结尾的，则返回传入的形参
	}
}

//案例五  返回值为两个函数类型
func calc(base int) (func(int) int, func(int) int) {
	add := func(i int) int {
		base += i
		return base
	}
	sub := func(i int) int {
		base -= i
		return base
	}
	return add, sub
}
func main() {
	// ret := adder2(100) //此处  ret等于add的返回值  即func(int) int。且100被addr2的形参x所接收
	// ret2 := ret(200)
	/*ret(200)等于执行匿名函数 func (y int) int  {  200被匿名函数的形参y所接收
		x+=y  此时  y=200 x在addr2外层函数中的参数或者外层函数中直接定义的变量。所以 x=100
		return x 300
	}
	*/
	// fmt.Println(ret2)
	ret := f3(f2, 100, 200) //把原来需要传递两个int类型的参数包装成一个不需要传参的函数(f2)
	f1(ret)                 //f3是返回无参函数类型，正好可以传给f1

	foo := getCalledNum(foo) //针对foo函数的计数器  在实际开发代码过程中，这里的返回函数尽量与原函数同名
	foo()                    //执行原有的foo函数，在执行装饰器函数
	//	案例四 加后缀
	jpgFunc := makeSuffixFunc(".jpg") //给makeSuffixFunc函数传入一个实参，被形参suffix接收，返回一个函数类型的返回值并赋值给jpgFunc函数
	fmt.Println(jpgFunc("test"))
	//	案例五  返回值为两个函数类型
	add, sub := calc(10)        //add sub分别对应calc函数的两个返回值类型为函数的返回值
	fmt.Println(add(1), sub(2)) //11 9  实际上每次执行完一个匿名函数时,形参base的值会被改变
	fmt.Println(add(3), sub(4)) //12 8
}
